﻿// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动！任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任！

using Admin.NET.Core.Service;
using Admin.NET.Application.Entity;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using NewLife.Caching;
using System.Drawing;
using DocumentFormat.OpenXml.Drawing.Diagrams;
using Furion.DatabaseAccessor;
namespace Admin.NET.Application;

/// <summary>
/// 通行记录服务
/// </summary>
[ApiDescriptionSettings(ApplicationConst.GroupName, Order = 100)]
public class UploadLogService : IDynamicApiController, ITransient
{
    private readonly SqlSugarRepository<UploadLog> _uploadLogRep;
    private readonly SqlSugarRepository<Device> _deviceRep;
    private readonly SqlSugarRepository<CaiJiLog> _caiJiLogRep;
    private readonly SqlSugarRepository<Employee> _employeeRep;
    private readonly SqlSugarRepository<CaiJiPhotos> _caiJiPhotosRep;
    private readonly SqlSugarRepository<CaiJiPhotosLogs> _CaiJiPhotosLogsRep;


    public UploadLogService(SqlSugarRepository<UploadLog> uploadLogRep, SqlSugarRepository<Device> deviceRep, SqlSugarRepository<CaiJiLog> caiJiLogRep, SqlSugarRepository<Employee> employeeRep, SqlSugarRepository<CaiJiPhotos> caiJiPhotosRep, SqlSugarRepository<CaiJiPhotosLogs> caiJiPhotosLogsRep)
    {
        _uploadLogRep = uploadLogRep;
        _deviceRep = deviceRep;
        _caiJiLogRep = caiJiLogRep;
        _employeeRep = employeeRep;
        _caiJiPhotosRep = caiJiPhotosRep;
        _CaiJiPhotosLogsRep = caiJiPhotosLogsRep;
    }

    /// <summary>
    /// 分页查询通行记录
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "Page")]
    [DisplayName("分页查询通行记录")]
    public async Task<SqlSugarPagedList<UploadLogOutput>> Page(UploadLogInput input)
    {
		input.SearchKey = input.SearchKey?.Trim();
        var query = _uploadLogRep.AsQueryable()
            //.Includes(u=>u.Employee)
            .WhereIF(!string.IsNullOrEmpty(input.SearchKey), u =>
                u.ClientSn.Contains(input.SearchKey)
                || u.cap_time.Contains(input.SearchKey)
                || u.EmpName.Contains(input.SearchKey)
                || u.ErrorMsg.Contains(input.SearchKey)
            )
            .WhereIF(input.ProjectId>0, u => u.ProjectId == input.ProjectId)
            .WhereIF(!string.IsNullOrWhiteSpace(input.ClientSn), u => u.ClientSn.Contains(input.ClientSn.Trim()))
            .WhereIF(input.EmployeeId>0, u => u.EmployeeId == input.EmployeeId)
            .WhereIF(!string.IsNullOrWhiteSpace(input.cap_time), u => u.cap_time.Contains(input.cap_time.Trim()))
            .WhereIF(!string.IsNullOrWhiteSpace(input.EmpName), u => u.EmpName.Contains(input.EmpName.Trim()))
            .WhereIF(!string.IsNullOrWhiteSpace(input.ErrorMsg), u => u.ErrorMsg.Contains(input.ErrorMsg.Trim()))
            //处理外键和TreeSelector相关字段的连接
            .LeftJoin<Project>((u, projectid) => u.ProjectId == projectid.Id )
            .OrderBy(u => u.CreateTime,OrderByType.Desc)
            .Select((u, projectid) => new UploadLogOutput
            {
                Id = u.Id,
                ProjectId = u.ProjectId, 
                ProjectIdName = projectid.Name,
                ClientSn = u.ClientSn,
                //Employee = u.Employee,
                EmployeeId = u.EmployeeId,
                cap_time = u.cap_time,
                EmpName = u.EmpName,
                ImageBase64 = u.ImageBase64,
                IsMobile = u.IsMobile,
                IsUpload = u.IsUpload,
                IsUploadErr = u.IsUploadErr,
                ErrorMsg = u.ErrorMsg,
                AuditingStatus = u.AuditingStatus,
                TenantId = u.TenantId,
                CreateOrgId = u.CreateOrgId,
                CreateOrgName = u.CreateOrgName,
                CreateTime = u.CreateTime,
                UpdateTime = u.UpdateTime,
                CreateUserId = u.CreateUserId,
                CreateUserName = u.CreateUserName,
                UpdateUserId = u.UpdateUserId,
                UpdateUserName = u.UpdateUserName,
                IsDelete = u.IsDelete,
            });
        //return await query.OrderBuilder(input, "u.").ToPagedListAsync(input.Page, input.PageSize);
        //return await query.OrderByDescending(u=>u.CreateTime).ToPagedListAsync(input.Page, input.PageSize);
        return await query.ToPagedListAsync(input.Page, input.PageSize);
    }

    /// <summary>
    /// 增加通行记录
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "Add")]
    [DisplayName("增加通行记录")]
    public async Task<long> Add(AddUploadLogInput input)
    {
        var entity = input.Adapt<UploadLog>();
        await _uploadLogRep.InsertAsync(entity);
        return entity.Id;
    }

    /// <summary>
    /// 删除通行记录
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "Delete")]
    [DisplayName("删除通行记录")]
    public async Task Delete(DeleteUploadLogInput input)
    {
        var entity = await _uploadLogRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
        await _uploadLogRep.FakeDeleteAsync(entity);   //假删除
        //await _uploadLogRep.DeleteAsync(entity);   //真删除
    }

    /// <summary>
    /// 更新通行记录
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "Update")]
    [DisplayName("更新通行记录")]
    public async Task Update(UpdateUploadLogInput input)
    {
        var entity = input.Adapt<UploadLog>();
        await _uploadLogRep.AsUpdateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
    }

    /// <summary>
    /// 获取通行记录
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpGet]
    [ApiDescriptionSettings(Name = "Detail")]
    [DisplayName("获取通行记录")]
    public async Task<UploadLog> Detail([FromQuery] QueryByIdUploadLogInput input)
    {
        return await _uploadLogRep.GetFirstAsync(u => u.Id == input.Id);
    }

    /// <summary>
    /// 获取通行记录列表
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpGet]
    [ApiDescriptionSettings(Name = "List")]
    [DisplayName("获取通行记录列表")]
    public async Task<List<UploadLogOutput>> List([FromQuery] UploadLogInput input)
    {
        return await _uploadLogRep.AsQueryable().Select<UploadLogOutput>().ToListAsync();
    }

    /// <summary>
    /// 获取项目ID列表
    /// </summary>
    /// <returns></returns>
    [ApiDescriptionSettings(Name = "ProjectProjectIdDropdown"), HttpGet]
    [DisplayName("获取项目ID列表")]
    public async Task<dynamic> ProjectProjectIdDropdown()
    {
        return await _uploadLogRep.Context.Queryable<Project>()
                .Select(u => new
                {
                    Label = u.Name,
                    Value = u.Id
                }
                ).ToListAsync();
    }

    /// <summary>
    /// 接收考勤数据
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    [HttpPost("/Emp/Log")]
    [AllowAnonymous]
    [NonUnify]
    public async Task<dynamic> LogRev(FaceInfo data)
    {
        //Console.WriteLine(JsonConvert.SerializeObject(data));
        Console.WriteLine("收到照片");
        Console.WriteLine(data.cap_time);

        try
        {
            if (string.IsNullOrEmpty(data.device_sn))
            {
                var AddMessageErr = new
                {
                    reply = "ACK",
                    cmd = "face",
                    code = 0,
                    sequence_no = data.sequence_no,
                    cap_time = data.cap_time,
                };
                return AddMessageErr;
            }

            var device = await _deviceRep.AsQueryable()
                .Includes(u => u.Project)
                .Where(u => u.ClientSn == data.device_sn)
                .FirstAsync();
            if (device == null) throw Oops.Oh("无法查到相应设备");

            //# 比对结果（0：未比对。-1：比对失败。大于0的取值：比对成功时的确信度分数（100分制）。）
            if (data.match_result <= 0 )
            {
                // 采集模式
                if (device.Project.CaiJiMoShi == true)
                {
                    //_caiJiLogRep
                    CaiJiLog caiJiLog = new CaiJiLog();
                    caiJiLog.ClientSn = data.device_sn;
                    caiJiLog.cap_time = data.cap_time;
                    caiJiLog.ImageBase64 = data.closeup_pic.data;
                    caiJiLog.ProjectId = device.ProjectId;
                    caiJiLog.TenantId = device.TenantId;
                    caiJiLog.CreateOrgId = device.CreateOrgId;
                    caiJiLog.CreateOrgName = device.CreateOrgName;
                    await _caiJiLogRep.InsertAsync(caiJiLog);

                    var AddMessageErr = new
                    {
                        reply = "ACK",
                        cmd = "face",
                        code = 0,
                        sequence_no = data.sequence_no,
                        cap_time = data.cap_time,
                    };
                    return AddMessageErr;
                }
                else
                {
                    var AddMessageErr = new
                    {
                        reply = "ACK",
                        cmd = "face",
                        code = 0,
                        sequence_no = data.sequence_no,
                        cap_time = data.cap_time,
                    };
                    return AddMessageErr;
                }
            }


            if (string.IsNullOrEmpty(data.device_sn) || string.IsNullOrEmpty(data.cap_time) || string.IsNullOrEmpty(data.match.person_id))
            {
                throw Oops.Oh("数据为空");
            }



            UploadLog SaveEmpLog = new UploadLog();
            SaveEmpLog.Id = Yitter.IdGenerator.YitIdHelper.NextId();
            SaveEmpLog.ClientSn = data.device_sn;
            SaveEmpLog.EmpSn = data.match.person_id;
            SaveEmpLog.EmpName = data.match.person_name;
            // SaveEmpLog.customer_text = data.match.customer_text;
            // SaveEmpLog.customer_text = data.match.person_id;
            SaveEmpLog.IsMobile = false;
            SaveEmpLog.cap_time = data.cap_time;
            //改特写图 closeup_pic
            SaveEmpLog.ImageBase64 = data.closeup_pic.data;
            //SaveEmpLog.ImageBase64 = data.overall_pic.data;

            SaveEmpLog.IsUpload = false;
            SaveEmpLog.IsUploadErr = false;
            SaveEmpLog.ErrorMsg = "";
            SaveEmpLog.AuditingStatus = AuditingStatusType.Yes;
            SaveEmpLog.ProjectId = device.ProjectId;
            SaveEmpLog.TenantId = device.TenantId;
            SaveEmpLog.CreateOrgId = device.CreateOrgId;
            SaveEmpLog.CreateTime = DateTime.Now;
            SaveEmpLog.CreateOrgName = device.CreateOrgName;
            await _uploadLogRep.InsertAsync(SaveEmpLog);

            string AddCommandId = Yitter.IdGenerator.YitIdHelper.NextId().ToString();
            var AddMessage = new
            {
                reply = "ACK",
                cmd = "face",
                code = 0,
                sequence_no = data.sequence_no,
                cap_time = data.cap_time,
            };

            return AddMessage;
        }
        catch (Exception e)
        {

            throw Oops.Oh("异常" + e.StackTrace + e.Message);
        }
    }


    #region 移动考勤

    /// <summary>
    /// 根据身份证号码取得项目列表及设备列表人员信息
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost("/api/UploadLog/GetAllInfo")]
    [AllowAnonymous]
    public async Task<object> GetAllInfo(GetAllInfoInput input)
    {
        if (string.IsNullOrEmpty(input.IdNum)) throw Oops.Oh("身份证号码不能为空！");
        var employee = await _employeeRep.AsQueryable()
            .ClearFilter()
            .Includes(u => u.Project).Includes(u=>u.Project.Devices)
            .Where(u => u.IdNum == input.IdNum)
            .FirstAsync(u => u.IdNum == input.IdNum);
        if (employee == null) throw Oops.Oh("无法查询到人员信息或该人员不允许此考勤方式！");

        if (employee.IsMobile == false) throw Oops.Oh("该人员不允许此考勤方式！");

        if (employee.Project == null) throw Oops.Oh("无法查询到项目信息！");
        if (employee.Project.Devices.Count <= 0) throw Oops.Oh("该项目无打卡设备！");

        return new { employee = employee, project = employee.Project };
    }

    /// <summary>
    /// 根据用户id获取通行记录列表
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost("/api/UploadLog/ListByIdCard")]
    [AllowAnonymous]
    public async Task<List<UploadLogOutput>> ListByIdCard(GetAllList input)
    {
        return await _uploadLogRep.AsQueryable()
            .ClearFilter()
            .WhereIF(!string.IsNullOrEmpty(input.id), u => u.EmployeeId == Convert.ToInt64(input.id) )
            .Select<UploadLogOutput>().ToListAsync();
    }

    // 添加移动考勤日志  
    [HttpPost("/api/UploadLog/addH5")]
    [AllowAnonymous]
    [UnitOfWork]
    public async Task AddH5(AddUploadLogInputH5 input)
    {
        Console.WriteLine(JsonConvert.SerializeObject(input));

        var employee = await ValidateEmployee(input.EmployeeId); // 验证员工ID是否有效,有就返回该员工信息  
        int clothesGroup = await DetermineClothesGroup(input.EmployeeId); // 取本次衣服组号
        string nowPhotoId = await GetNextPhotoId(input, clothesGroup); // 获取当前照片ID  
        string base64Img = await GetImageBase64(nowPhotoId); // 获取照片的Base64编码  



        var uploadLog = input.Adapt<UploadLog>(); // 将输入适配为上传日志对象  
        uploadLog.IsMobile = true; // 设置为移动端上传  
        uploadLog.EmpSn = employee.ZjEmpID;
        uploadLog.ImageBase64 = base64Img; // 设置图片的Base64编码  
        uploadLog.CreateTime = DateTime.Now; // 设置创建时间  
        uploadLog.AuditingStatus = employee.IsAutoAudit ? AuditingStatusType.Yes : AuditingStatusType.Not; // 设置审核状态  
        await _uploadLogRep.InsertAsync(uploadLog); // 异步插入上传日志  

        var newDKLog = new CaiJiPhotosLogs // 创建新的采集照片日志  
        {
            EmployeeId = input.EmployeeId, // 设置员工ID  
            ClothesGroup = clothesGroup, // 设置衣服组  
            DKDate = DateTime.Now.ToString("yyyy-MM-dd"), // 设置打卡日期  
            Direction = input.Direction, // 设置方向  
            CreateTime = DateTime.Now, // 设置创建时间  
            PhotoId = nowPhotoId // 设置照片ID  
        };

        await _CaiJiPhotosLogsRep.InsertAsync(newDKLog); // 异步插入采集照片日志  
    }

    private async Task<string> GetNextPhotoId(AddUploadLogInputH5 input, int clothesGroup)
    {
        var lastUploadLog = await _CaiJiPhotosLogsRep  // 查找最后打卡记录的照片  
            .AsQueryable()
            .ClearFilter() // 清除过滤条件  
            .OrderBy(u => u.CreateTime, OrderByType.Desc) // 倒序
            .FirstAsync(u => u.EmployeeId == input.EmployeeId && u.Direction == input.Direction && u.ClothesGroup == clothesGroup);

                //?.空值条件运算符,首先检查 lastUploadLog 是否为 null。如果 lastUploadLog 不为 null，则返回 lastUploadLog.PhotoId 的值；如果 lastUploadLog 为 null，则返回 null。
                //?? 0 是空合并运算符。它表示如果前面的表达式（即 lastUploadLog?.PhotoId）为 null，则返回 0；否则返回前面的值。
                string LastPhotoId = lastUploadLog?.PhotoId ?? "";

                var empPhotos = await _caiJiPhotosRep.AsQueryable()  // 查找考勤照片
                     .ClearFilter() // 清除过滤条件  
                     .FirstAsync(u => u.EmployeeId == input.EmployeeId && u.Direction == input.Direction && u.ClothesGroup == clothesGroup);

                if (empPhotos.Photos.Count == 0) // 检查照片是否存在  
                    throw Oops.Oh("无法查询该人员通行图片库！"); // 抛出异常  

        return string.IsNullOrWhiteSpace(LastPhotoId) ? empPhotos.Photos[0] : GetNextPhoto(empPhotos.Photos, LastPhotoId); // 本的图片ID 
    }

    // 验证员工ID  
    private async Task<Employee> ValidateEmployee(long employeeId)
    {
        if (employeeId <= 0) // 检查员工ID是否有效  
            throw Oops.Oh("人员ID不能为空！"); // 抛出异常  

        var employee = await _employeeRep.AsQueryable()
            .ClearFilter() // 清除过滤条件  
            .FirstAsync(u => u.Id == employeeId); // 查找员工 
        if (employee == null)
        {
            throw Oops.Oh("人员信息为空！"); // 抛出异常  
        }

        if (!employee.IsMobile)
        {
            throw Oops.Oh("人员不允许移动考勤！"); // 抛出异常  
        }
        return employee;
    }

    // 确定衣服组  
    private async Task<int> DetermineClothesGroup(long employeeId)
    {
        // 取得员工的所有衣服组
        var clothesGroupList = await _caiJiPhotosRep.AsQueryable()
            .Where(e => e.EmployeeId == employeeId) // 根据员工ID过滤
            .OrderBy(e => e.ClothesGroup, OrderByType.Asc) // 正序
            .Select(e => e.ClothesGroup) // 选择衣服组  
            .Distinct() // 去重  
            .ToListAsync(); // 异步获取列表  

        if (clothesGroupList.Count <= 0) //  
            throw Oops.Oh("无衣服分组！"); // 抛出异常  

        //clothesGroupList.Sort(); // 对衣服组进行排序  

        var lastUploadLog = await _CaiJiPhotosLogsRep  // 获取最后的上传日志,First() 等同于C#中的 FirstOrDefault , 没有值返回 null  
            .AsQueryable()
            .ClearFilter()
            .OrderBy(u => u.CreateTime,OrderByType.Desc) // 倒序
            .FirstAsync(u => u.EmployeeId == employeeId);

        // 判断用哪组图片
        if (lastUploadLog != null)
        {
            // 是否同一天
            bool isSameDay = DateTime.Now.ToString("yyyy-MM-dd") == lastUploadLog.DKDate;
            return  isSameDay ? lastUploadLog.ClothesGroup : GetNextValue(lastUploadLog.ClothesGroup, clothesGroupList);
        }
        else
        {
            return  1;
        }
    }

    // 取本次打卡图像
    private async Task<string> GetImageBase64(string photoId)
    {

        var path = Path.Combine(App.WebHostEnvironment.WebRootPath, $"images\\CaiJiPhotos\\{photoId}.jpg");

        // 将图像转换为Base64编码
        try
        {
            // 使用Bitmap读取图像文件  
            using (Bitmap bmp = new Bitmap(path)) // 创建Bitmap对象  
            using (MemoryStream ms = new MemoryStream()) // 创建内存流  
            {
                // 将图像保存到内存流中，格式为JPEG  
                bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
                // 将内存流转换为Base64字符串并返回  
                return Convert.ToBase64String(ms.ToArray());
            }
        }
        catch (Exception ex)
        {
            // 记录异常日志（可根据需要实现日志记录）  
            Console.WriteLine($"Error converting image to Base64: {ex.Message}");
            return null; // 返回null表示转换失败  
        }
    }

    // 获取下一个照片ID  
    private string GetNextPhoto(List<string> photos, string lastPhotoId)
    {
        int index = photos.IndexOf(lastPhotoId);
        if (index == -1)
        {
            // 输入值不在列表中，返回默认值
            return photos[0];
        }
        // 如果输入值是列表中的最后一个元素，则返回列表的第一个元素，否则返回下一个元素
        return index == photos.Count - 1 ? photos[0] : photos[index + 1];
    }

    // 获取下一个衣服组  
    public int GetNextValue(int input, List<int> list)
    {
        int index = list.IndexOf(input);
        if (index == -1)
        {
            // 输入值不在列表中，返回默认值
            return 1;
        }
        // 如果输入值是列表中的最后一个元素，则返回列表的第一个元素，否则返回下一个元素
        return index == list.Count - 1 ? list[0] : list[index + 1];
    }


    #endregion

}

/// <summary>
/// 根据身份证号码取得项目列表及设备列表人员信息
/// </summary>
public class GetAllInfoInput
{
    /// <summary>
    /// 身份证号码
    /// </summary>
    public string IdNum { get; set; }

}

/// <summary>
/// 取用户打卡记录
/// </summary>
public class GetAllList
{
    /// <summary>
    /// 用户id
    /// </summary>
    public string id { get; set; }

}